﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Caching;
using SqlSugar;
using StackExchange.Redis;

namespace Xwltz.Framework.Helper
{
    public class CacheHelper
    {
        #region Cache操作

        private static readonly Cache Cache = HttpRuntime.Cache;

        /// <summary>
        /// 获取所有缓存对象
        /// </summary>
        /// <returns></returns>
        public static IDictionaryEnumerator GetEnumerable()
        {
            return Cache.GetEnumerator();
        }

        /// <summary>
        /// 根据Key查询缓存是否存在
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static bool Exist(string key)
        {
            return Cache.Get(key) != null;
        }

        /// <summary>
        /// 根据Key获取缓存对象
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static object GetCache(string key)
        {
            return Cache[key];
        }

        /// <summary>
        /// 根据Key获取缓存对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public static T GetCache<T>(string key) where T : class
        {
            if (Cache[key] != null)
            {
                return (T)Cache[key];
            }
            return default(T);
        }

        public static T GetCache<T>(string key, Func<T> func)
        {
            T t;
            if (Cache[key] != null)
            {
                t = func.Invoke();
                Cache.Insert(key, t);
            }
            else
            {
                t = (T)Cache[key];
            }

            //GetCache<int>("test", new Func<int>(() => { return 18; }));
            //var i  = GetCache("test", () => 18);

            return t;
        }

        /// <summary>
        /// 根据Key设置缓存对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static void SetCache<T>(string key, T value) where T : class
        {
            Cache.Insert(key, value, null, DateTime.Now.AddMinutes(1440), TimeSpan.Zero);
        }

        /// <summary>
        /// 根据Key设置缓存对象并设置过期时间
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="expires"></param>
        public static void SetCache<T>(string key, T value, DateTime expires) where T : class
        {
            Cache.Insert(key, value, null, expires, TimeSpan.Zero);
        }

        /// <summary>
        /// 根据Key设置缓存对象
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static void SetCache(string key, object value)
        {
            Cache.Insert(key, value, null, DateTime.Now.AddMinutes(1440), TimeSpan.Zero);
        }

        /// <summary>
        /// 根据Key设置缓存对象并设置过期时间
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="expires"></param>
        public static void SetCache(string key, object value, DateTime expires)
        {
            Cache.Insert(key, value, null, expires, TimeSpan.Zero);
        }

        /// <summary>
        /// 根据Key移除缓存对象
        /// </summary>
        /// <param name="key"></param>
        public static void RemoveCache(string key)
        {
            Cache.Remove(key);
        }

        /// <summary>
        /// 移除所有缓存对象
        /// </summary>
        public static void RemoveCache()
        {
            var cacheEnum = Cache.GetEnumerator();
            while (cacheEnum.MoveNext())
            {
                Cache.Remove(cacheEnum.Key.ToString());
            }
        }

        /// <summary>
        /// 扩展缓存对象
        /// </summary>
        /// <param name="func"></param>
        public static void RemoveCache(Func<string, bool> func)
        {
            var keyList = new List<string>();
            foreach (var key in Cache)
            {
                if (func.Invoke(key.ToString()))
                {
                    keyList.Add(key.ToString());
                }
            }
            keyList.ForEach(k => Cache.Remove(k));
        }

        #endregion
    }

    #region HttpRuntime.Cache

    public class HttpRuntimeCache : ICacheService
    {
        public void Add<TV>(string key, TV value)
        {
            HttpRuntimeCacheHelper<TV>.GetInstance().Add(key, value);
        }

        public void Add<TV>(string key, TV value, int cacheDurationInSeconds)
        {
            HttpRuntimeCacheHelper<TV>.GetInstance().Add(key, value, cacheDurationInSeconds);
        }

        public bool ContainsKey<TV>(string key)
        {
            return HttpRuntimeCacheHelper<TV>.GetInstance().ContainsKey(key);
        }

        public TV Get<TV>(string key)
        {
            return HttpRuntimeCacheHelper<TV>.GetInstance().Get(key);
        }

        public IEnumerable<string> GetAllKey<TV>()
        {
            return HttpRuntimeCacheHelper<TV>.GetInstance().GetAllKey();
        }

        public TV GetOrCreate<TV>(string cacheKey, Func<TV> create, int cacheDurationInSeconds = int.MaxValue)
        {
            var cacheManager = HttpRuntimeCacheHelper<TV>.GetInstance();
            if (cacheManager.ContainsKey(cacheKey))
            {
                return cacheManager[cacheKey];
            }
            else
            {
                var result = create();
                cacheManager.Add(cacheKey, result, cacheDurationInSeconds);
                return result;
            }
        }

        public void Remove<TV>(string key)
        {
            HttpRuntimeCacheHelper<TV>.GetInstance().Remove(key);
        }
    }
    internal class HttpRuntimeCacheHelper<V>
    {

        #region 全局变量
        private static HttpRuntimeCacheHelper<V> _instance = null;
        private static readonly object InstanceLock = new object();
        #endregion

        #region 构造函数

        private HttpRuntimeCacheHelper() { }
        #endregion

        #region  属性
        /// <summary>         
        ///根据key获取value     
        /// </summary>         
        /// <value></value>      
        public V this[string key] => (V)HttpRuntime.Cache[CreateKey(key)];

        #endregion

        #region 公共函数

        /// <summary>         
        /// key是否存在       
        /// </summary>         
        /// <param name="key">key</param>         
        /// <returns> ///  存在<c>true</c> 不存在<c>false</c>.        /// /// </returns>         
        public bool ContainsKey(string key)
        {
            return HttpRuntime.Cache[CreateKey(key)] != null;
        }

        /// <summary>         
        /// 获取缓存值         
        /// </summary>         
        /// <param name="key">key</param>         
        /// <returns></returns>         
        public V Get(string key)
        {
            return (V)HttpRuntime.Cache.Get(CreateKey(key));
        }

        /// <summary>         
        /// 获取实例 （单例模式）       
        /// </summary>         
        /// <returns></returns>         
        public static HttpRuntimeCacheHelper<V> GetInstance()
        {
            if (_instance != null) return _instance;
            lock (InstanceLock)
                if (_instance == null)
                    _instance = new HttpRuntimeCacheHelper<V>();
            return _instance;
        }

        /// <summary>         
        /// 插入缓存(默认20分钟)        
        /// </summary>         
        /// <param name="key"> key</param>         
        /// <param name="value">value</param>          
        public void Add(string key, V value)
        {
            Add(key, value, 60 * 20);
        }

        /// <summary>         
        /// 插入缓存        
        /// </summary>         
        /// <param name="key"> key</param>         
        /// <param name="value">value</param>         
        /// <param name="cacheDurationInSeconds">过期时间单位秒</param>         
        public void Add(string key, V value, int cacheDurationInSeconds)
        {
            Add(key, value, cacheDurationInSeconds, CacheItemPriority.Default);
        }

        /// <summary>         
        /// 插入缓存.         
        /// </summary>         
        /// <param name="key">key</param>         
        /// <param name="value">value</param>         
        /// <param name="cacheDurationInSeconds">过期时间单位秒</param>         
        /// <param name="priority">缓存项属性</param>         
        public void Add(string key, V value, int cacheDurationInSeconds, CacheItemPriority priority)
        {
            string keyString = CreateKey(key);
            HttpRuntime.Cache.Insert(keyString, value, null,
            DateTime.Now.AddSeconds(cacheDurationInSeconds), Cache.NoSlidingExpiration, priority, null);
        }

        /// <summary>         
        /// 插入缓存.         
        /// </summary>         
        /// <param name="key">key</param>         
        /// <param name="value">value</param>         
        /// <param name="cacheDurationInSeconds">过期时间单位秒</param>
        /// <param name="dependency"></param>
        /// <param name="priority">缓存项属性</param>         
        public void Add(string key, V value, int cacheDurationInSeconds, CacheDependency dependency, CacheItemPriority priority)
        {
            string keyString = CreateKey(key);
            HttpRuntime.Cache.Insert(keyString, value,
             dependency, DateTime.Now.AddSeconds(cacheDurationInSeconds), Cache.NoSlidingExpiration, priority, null);
        }

        /// <summary>         
        /// 删除缓存         
        /// </summary>         
        /// <param name="key">key</param>         
        public void Remove(string key)
        {
            HttpRuntime.Cache.Remove(CreateKey(key));
        }

        /// <summary>
        /// 清除所有缓存
        /// </summary>
        public void RemoveAll()
        {
            var cache = HttpRuntime.Cache;
            IDictionaryEnumerator cacheEnum = cache.GetEnumerator();
            ArrayList al = new ArrayList();
            while (cacheEnum.MoveNext())
            {
                al.Add(cacheEnum.Key);
            }
            foreach (string key in al)
            {
                cache.Remove(key);
            }
        }

        /// <summary>
        /// 清除所有包含关键字的缓存
        /// </summary>
        /// <param name="removeExpression"></param>
        public void RemoveAll(Func<string, bool> removeExpression)
        {
            var allKeyList = GetAllKey();
            var delKeyList = allKeyList.Where(removeExpression).ToList();
            foreach (var key in delKeyList)
            {
                HttpRuntime.Cache.Remove(key);
            }
        }

        /// <summary>
        /// 获取所有缓存key
        /// </summary>
        /// <returns></returns>
        public IEnumerable<string> GetAllKey()
        {
            IDictionaryEnumerator cacheEnum = HttpRuntime.Cache.GetEnumerator();
            while (cacheEnum.MoveNext())
            {
                if (cacheEnum.Key != null) yield return cacheEnum.Key.ToString();
            }
        }
        #endregion

        #region 私有函数

        /// <summary>         
        ///创建KEY   
        /// </summary>         
        /// <param name="key">Key</param>         
        /// <returns></returns>         
        private string CreateKey(string key)
        {
            return key;
        }
        #endregion
    }

    #endregion

    #region Redis.Cache

    public class RedisCache : ICacheService
    {
        private readonly ServiceStackRedis _service;
        public RedisCache(string host, int port, string password, int expirySeconds, int db)
        {
            _service = new ServiceStackRedis(host, port, password, expirySeconds, db);
        }

        public RedisCache(string host)
        {
            _service = new ServiceStackRedis(host);
        }

        public RedisCache()
        {
            _service = new ServiceStackRedis();
        }

        public void Add<TV>(string key, TV value)
        {
            _service.Set(key, value);
        }

        public void Add<TV>(string key, TV value, int cacheDurationInSeconds)
        {
            _service.Set(key, value, TimeSpan.FromSeconds(cacheDurationInSeconds));
        }

        public bool ContainsKey<TV>(string key)
        {
            return _service.ContainsKey(key);
        }

        public TV Get<TV>(string key)
        {
            return _service.Get<TV>(key);
        }

        public IEnumerable<string> GetAllKey<TV>()
        {
            return _service.GetAllKeys();
        }

        public TV GetOrCreate<TV>(string cacheKey, Func<TV> create, int cacheDurationInSeconds = int.MaxValue)
        {
            if (this.ContainsKey<TV>(cacheKey))
            {
                return this.Get<TV>(cacheKey);
            }
            else
            {
                var result = create();
                this.Add(cacheKey, result, cacheDurationInSeconds);
                return result;
            }
        }

        public void Remove<TV>(string key)
        {
            _service.Remove(key);
        }
    }

    public class ServiceStackRedis
    {
        private readonly int _expirySeconds;
        private static ConnectionMultiplexer _redisClientManager;
        private static string _connectStr = string.Empty;
        private static IDatabase _db;
        private static readonly SerializeService SerializeService = new SerializeService();
        private static readonly object Locker = new object();

        public static ConnectionMultiplexer GetRedisClient()
        {
            if (_redisClientManager != null) return _redisClientManager;
            lock (Locker)
            {
                if (_redisClientManager == null || !_redisClientManager.IsConnected)
                {
                    _redisClientManager = ConnectionMultiplexer.Connect(_connectStr);
                }
            }
            return _redisClientManager;
        }
        
        public ServiceStackRedis(string host, int port, string password, int expirySeconds, int db)
        {
            _expirySeconds = expirySeconds;
            _connectStr = $"{host}:{port},defaultDatabase={db},password={password},allowadmin=true,abortConnect=false";
            _redisClientManager = GetRedisClient();
            _db = _redisClientManager.GetDatabase(db);
        }

        public ServiceStackRedis(string host) : this(host, 6379, null, -1, 0)
        {
        }

        public ServiceStackRedis() : this("localhost", 6379, null, -1, 0)
        {
        }

        public bool Set(string key, object value)
        {
            if (key.IsNullOrEmpty()) return false;

            if (_expirySeconds != -1) return Set(key, value, TimeSpan.FromSeconds(_expirySeconds));

            var json = SerializeService.SerializeObject(value);
            return _db.StringSet(key, json);
        }

        public bool Set(string key, object value, TimeSpan expiry)
        {
            if (key.IsNullOrEmpty()) return false;

            var json = SerializeService.SerializeObject(value);
            return _db.StringSet(key, json, expiry);
        }

        public T Get<T>(string key)
        {
            if (key.IsNullOrEmpty()) return default;

            string data = _db.StringGet(key);
            return data == null ? default : SerializeService.DeserializeObject<T>(data);
        }

        public bool Remove(string key)
        {
            return _db.KeyDelete(key);
        }

        public bool ContainsKey(string key)
        {
            return _db.KeyExists(key);
        }

        public List<string> GetAllKeys(string key = "SqlSugarDataCache.*")
        {
            var result = new List<string>();
            HashEntry[] arr = _db.HashGetAll(key);
            foreach (var item in arr)
            {
                if (!item.Value.IsNullOrEmpty)
                {
                    result.Add(SerializeService.DeserializeObject<string>(item.Value));
                }
            }
            return result;
        }
    }

    #endregion
}
